#include "PluginManager.h"
#include "DynLibManager.h"
#include "DynLib.h"

#include "../fuLogger.h"

#include <functional>
#include <algorithm>
#include <list>

BEGIN_NAMESPACE_CPPX

typedef void (*DLL_CONFIG_PLUGIN)(void);
typedef void (*DLL_START_PLUGIN)(void);
typedef void (*DLL_STOP_PLUGIN)(void);

class DynLib;

class _CPPX_API PluginInfo_t
{
    friend class PluginManager;
    DynLib *            pLib;
    DLL_CONFIG_PLUGIN   dllConfigPlugin;
    DLL_START_PLUGIN    dllStartPlugin;
    DLL_STOP_PLUGIN     dllStopPlugin;

public:
    std::string PluginName;
    bool IsRunning;

public:
    PluginInfo_t(void) : dllConfigPlugin(0), dllStartPlugin(0), dllStopPlugin(0)
    {}
    ~PluginInfo_t(void)
    {}

    bool ConfigPlugin(void){
        if( NULL == dllConfigPlugin ){
            fuLoggerRef() << "PluginManager : Cannot find symbol [dllConfigPlugin] in library " << pLib->getName() << fuEnter();
            return false;
        }
        fuLoggerRef() << "PluginManager : Plugin " << pLib->getName() << " is configuring." << fuEnter();
        dllConfigPlugin();
        fuLoggerRef() << "PluginManager : Plugin " << pLib->getName() << " is configured." << fuEnter();
        return true;
    }
    bool StartPlugin(void){
        if( NULL == dllStartPlugin ){
            fuLoggerRef() << "PluginManager : Cannot find symbol [dllStartPlugin] in library " << pLib->getName() << fuEnter();
            return false;
        }
        fuLoggerRef() << "PluginManager : Plugin " << pLib->getName() << " is starting." << fuEnter();
        IsRunning = true;
        dllStartPlugin();
        fuLoggerRef() << "PluginManager : Plugin " << pLib->getName() << " is started." << fuEnter();
        return true;
    }
    bool StopPlugin(void){
        if( NULL == dllStopPlugin ){
            fuLoggerRef() << fuError("PluginManager : Cannot find symbol [dllStopPlugin] in library ") << pLib->getName() << fuEnter();
            return false;
        }
        fuLoggerRef() << "PluginManager : Plugin " << pLib->getName() << " is stoping." << fuEnter();
        IsRunning = false;
        dllStopPlugin();
        fuLoggerRef() << "PluginManager : Plugin " << pLib->getName() << " is stoped." << fuEnter();
        return true;
    }
};

//////////////////////////////////////////////////////////////////////////
typedef std::map<std::string,PluginInfo_t>   PluginMap_t;

struct PluginManager::pimpl_t
{
	DynLibManager	dll_mgr;
    PluginMap_t     PluginMap;
};

PluginManager::PluginManager( void ) :
pimpl_(new pimpl_t)
{

}

PluginManager::~PluginManager( void )
{
	unloadPlugins();
}

bool PluginManager::findPlugin(const std::string & pluginName)
{
    // Check if plugin is loaded or not.
    if( NULL != pimpl_->dll_mgr.find_dynlib(pluginName) ){
        return true;
    }
    return false;
}

bool PluginManager::loadPlugin( const std::string & pluginName )
{
	// Check if plugin is loaded or not.
	if( NULL != pimpl_->dll_mgr.find_dynlib(pluginName) ){
		return true;
	}

	// Load plugin library.
	DynLib * pLib = pimpl_->dll_mgr.load_dynlib(pluginName);
	if( NULL == pLib ){
		return false;
	}

    // Get PluginInfo
    PluginInfo_t info;
    info.PluginName         = pluginName;
    info.pLib               = pLib;
    info.dllConfigPlugin    = (DLL_CONFIG_PLUGIN)pLib->GetSymbol("dllConfigPlugin");
    info.dllStartPlugin     = (DLL_START_PLUGIN) pLib->GetSymbol("dllStartPlugin");
    info.dllStopPlugin      = (DLL_STOP_PLUGIN)  pLib->GetSymbol("dllStopPlugin");

	// Store for later unload
    pimpl_->PluginMap[pluginName]   = info;
	return pimpl_->PluginMap[pluginName].StartPlugin();

}

bool PluginManager::unloadPlugin( const std::string & pluginName )
{
    PluginMap_t::iterator iter = pimpl_->PluginMap.find(pluginName);
    if( iter != pimpl_->PluginMap.end() ){
        iter->second.StopPlugin();
        pimpl_->dll_mgr.unload_dynlib(iter->second.pLib);        // Unload library & destroy
        pimpl_->PluginMap.erase(iter);
        return true;
    }
    //fuLoggerRef() << fuError("PluginManager::unloadPlugin() Plugin <") << pluginName << "> does not exists." << fuEnter();
    return false;
}

void PluginManager::unloadPlugins( void )
{
	// Unload plugins in reverse order to enforce dependencies
    for( PluginMap_t::reverse_iterator iter = pimpl_->PluginMap.rbegin(); iter != pimpl_->PluginMap.rend(); ++iter ){
        iter->second.StopPlugin();
        pimpl_->dll_mgr.unload_dynlib(iter->second.pLib);        // Unload library & destroy
    }
    pimpl_->PluginMap.clear();
    //fuLoggerRef() << "PluginManager : All plugins are unloaded." << fuEnter();
}

END_NAMESPACE_CPPX
